﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Tosi
{
    class GeneticAlgorithm
    {
        private List<Population> Pop;
        private Population NewPop;
        private double[] tempControl;
        private StatTable ST;
        private Evaluation eval;


        public GeneticAlgorithm()
        {
            tempControl = new double[6];
            Pop = new List<Population>();
            NewPop = new Population();
            Pop.Add(NewPop);
            setRandPopIni(Pop[0]);
            maxIndividual(Pop[0]);
            /*for (int i = 0; i < Pop[0].IndividList[0].I.Length; i++) {
                Pop[0].IndividList[0].I[i].setGroupTOSI(1);
            }*/
            //maxIndividual(Pop[0]);
            geneticAlgCycle();
        }


        private void maxIndividual(Population P)
        {
            double maxTOSI = P.IndividList[0].getTOSI();
            int maxIndex = 0;
            for (int i = 1; i < 4; i++)
            {
                if (P.IndividList[i].getTOSI() > maxTOSI)
                {
                    maxTOSI = P.IndividList[i].getTOSI();
                    maxIndex = i;
                }
            }
            P.setBestSettingIndivid(maxIndex);
        }
        private void testIndivid(int i)
        {
            Var.excelWriter.writeIndividual(i);
            Var.excelWriter.writeHeader1(i);
            for (int j = 0; j < Var.StrNumber; j++)
            {
                Var.excelWriter.writeWeight(j, Var.STREETS[j].getMyLight().getWeights());
            }
            Var.excelWriter.lineFeed1();

            eval = new Evaluation(this, i);
            eval.Start();
            Var.excelWriter.writeHeader2();
            for (int j = 0; j < Pop[Pop.Count - 1].IndividList[i].I.Length; j++)
            {
                for (int k = 0; k < Pop[Pop.Count - 1].IndividList[i].I[j].Group.Length; k++)
                {
                    double[] temp = new double[3];
                    temp[0] = Pop[Pop.Count - 1].IndividList[i].I[j].Group[k].getWaitingCars();
                    temp[1] = Pop[Pop.Count - 1].IndividList[i].I[j].Group[k].getWaitingTime();
                    temp[2] = Pop[Pop.Count - 1].IndividList[i].I[j].Group[k].getStreetLoad();
                    Var.excelWriter.writeLightTosi(Pop[Pop.Count - 1].IndividList[i].I[j].Group[k].myLight.getId(), temp);
                }
            }
            Var.excelWriter.lineFeed3();
            Var.excelWriter.writeHeader3();
            for (int j = 0; j < Pop[Pop.Count - 1].IndividList[i].I.Length; j++)
            {
                double[] temp = new double[4];
                temp[0] = Pop[Pop.Count - 1].IndividList[i].I[j].getWaitingCarsAvg();
                temp[1] = Pop[Pop.Count - 1].IndividList[i].I[j].getWaitingTimeAvg();
                temp[2] = Pop[Pop.Count - 1].IndividList[i].I[j].getJunctionCongestion();
                temp[3] = Pop[Pop.Count - 1].IndividList[i].I[j].getGroupTOSI();
                Var.excelWriter.writeJuncTosi(j, temp);
            }
            Var.excelWriter.lineFeed2();
            Var.excelWriter.footer(Pop[Pop.Count - 1].IndividList[i].getTOSI());
        }
        public void geneticAlgCycle() {
            for (int gen = 1; gen < 44; gen++)
            {
                Var.excelWriter.writeGeneration(gen);
                for (int i = 0; i < 5; i++)
                {
                    if (i == 0) ParentSelection();
                    else if (i == 4) Crossover();
                    else Mutation(i);

                    testIndivid(i);
                    // pdfWriter.fill(getAllWeights(), 6, Var.StrNumber, i);
                    
                }
                maxIndividual(Pop[Pop.Count - 1]);
                //pdfWriter.closeFile();
            }
        }
        private double[,] getAllWeights() {
            double[,] temp = new double[6, Var.StrNumber];
            for (int i = 0; i < Var.StrNumber; i++) {
                double[] tempi = Var.STREETS[i].getMyLight().getWeights();
                for (int j=0; j<6; j++){
                    temp[j,i] = tempi[j];
                }
            }
            return temp;
        }
        private void setRandPopIni(Population pop)
        {
            for (int i = 0; i < pop.IndividList.Length; i++)
            {
                //LightGroup tempLightGrp = Ind.getIndividual()[i];
                setRandIndividIni(pop.IndividList[i]);
                testIndivid(i);
            }
        }
        private void setRandIndividIni(Individual Ind)
        {
            for (int i = 0; i < Ind.I.Length; i++)
            {
                //LightGroup tempLightGrp = Ind.getIndividual()[i];
                setRandLightGrpIni(Ind.I[i]);
            }
        }
        private void setRandLightGrpIni(LightGroup LG)
        {
            for (int i = 0; i < LG.Group.Length; i++)
            {
                RandControls();
                LG.Group[i].setLightControls(tempControl);
            }
        }
        public void setStatTable(ref StatTable ST1)
        {
            ST = ST1;
        }      
        private void RandControls()
        {
            for (int i = 0; i < 6; i++)
                tempControl[i] = Var.varRandom.getRandomNr(1, 10);
        }
        public void Fitness(int index)
        {
            for (int j = 0; j < Pop[Pop.Count - 1].IndividList[index].I.Length; j++)
            {
                for (int k = 0; k < Pop[Pop.Count - 1].IndividList[index].I[j].Group.Length; k++)
                {
                    Pop[Pop.Count - 1].IndividList[index].I[j].Group[k].allTOSI();
                }
                Pop[Pop.Count - 1].IndividList[index].I[j].countTOSI();
            }
            Pop[Pop.Count-1].IndividList[index].countTOSI();
        }
        public void intervalFitness(int index)
        {
            for (int j = 0; j < Pop[Pop.Count - 1].IndividList[index].I.Length; j++)
            {
                for (int k = 0; k < Pop[Pop.Count - 1].IndividList[index].I[j].Group.Length; k++)
                {
                    int streetIndex = Var.JUNCTIONS[j].inStreets[k].myLight.getId();
                    Pop[Pop.Count - 1].IndividList[index].I[j].Group[k].countWaitingCars(ST.AvgWaitingCarsPerStreet[streetIndex]);
                    Pop[Pop.Count - 1].IndividList[index].I[j].Group[k].countWaitingTime(ST.SumWaitingTimePerStreet[streetIndex]);
                    Pop[Pop.Count - 1].IndividList[index].I[j].Group[k].countStreetLoad(ST.AvgStreetLoad[streetIndex]);
                }
                Pop[Pop.Count - 1].IndividList[index].I[j].countWaitingCarsAvg(ST.AvgWaitingCarsPerJunction[j]);
                Pop[Pop.Count - 1].IndividList[index].I[j].countWaitingTimeAvg(ST.AvgWaitingTimePerJunction[j]);
                Pop[Pop.Count - 1].IndividList[index].I[j].countJunctionCongestion(ST.IntJunctionCongestion[j]);
                /*Console.WriteLine("{0}.VALUES: {1} - {2} - {3} - {4}", j, ST.SumWaitingTime[j], 
                    ST.AvgWaitingTime[j], ST.IntJunctionCongestion[j], ST.AvgJunctionLoad[j]);
                Console.WriteLine("\t: {0} - {1} - {2} - {3}",
                    Pop[Pop.Count - 1].IndividList[index].I[j].getWaitTimeSum(),
                    Pop[Pop.Count - 1].IndividList[index].I[j].getWaitTimeAvg(),
                    Pop[Pop.Count - 1].IndividList[index].I[j].getJunctionCongestion(),
                    Pop[Pop.Count - 1].IndividList[index].I[j].getStreetLoad());
                Console.ReadKey();                */
            }
        }
        private int maxTOSILightGroup(int index)
        {
            double maxTOSI = Pop[Pop.Count - 1].IndividList[0].I[index].getGroupTOSI();
            //LightGroup tempLightGrp = Pop.getPopulationElement(0).getIndividual()[index];
            Console.WriteLine();
            Console.Write("[ {0},",Pop[Pop.Count - 1].IndividList[0].I[index].getGroupTOSI());
            int ind = 0;
            for (int i = 1; i < 5; i++)
            {
                Console.Write(" {0},", Pop[Pop.Count - 1].IndividList[i].I[index].getGroupTOSI());
                if (maxTOSI < Pop[Pop.Count - 1].IndividList[i].I[index].getGroupTOSI())
                {
                    maxTOSI = Pop[Pop.Count - 1].IndividList[i].I[index].getGroupTOSI();
                    ind = i;
                }
            }
            Console.WriteLine(" ] -> {0}", ind);
            Console.ReadKey();
            return ind;
        }
        public void nextGeneration()
        {
            NewPop = new Population();
        }
        public void ParentSelection()
        {
            NewPop = new Population();
            for (int i = 0; i < Var.JncNumber; i++)
            {
                NewPop.IndividList[0].I[i].setTo(Pop[Pop.Count-1].IndividList[maxTOSILightGroup(i)].I[i]);
            }
            Pop.Add(NewPop);
        }
        public void Mutation(int index)
        {
            for (int i = 0; i < Var.JncNumber; i++)
            {
                for (int j = 0; j < Var.JUNCTIONS[i].inStreets.Count; j++)
                {
                    tempControl = Pop[Pop.Count - 2].IndividList[0].I[i].Group[j].getLightControls();
                    int changeValue = Var.varRandom.getRandomNr(-2, 2);
                    int changeId = Var.varRandom.getRandomNr(0, 2);
                    if ((tempControl[changeId] + changeValue > 25) ||
                        (tempControl[changeId] + changeValue < 0))
                    {
                        tempControl[changeId] -= changeValue;
                    }
                    else
                    {
                        tempControl[changeId] += changeValue;
                    }
                    changeValue = Var.varRandom.getRandomNr(-2, 2);
                    changeId = Var.varRandom.getRandomNr(3, 5);
                    if ((tempControl[changeId] + changeValue > 25) ||
                        (tempControl[changeId] + changeValue < 0))
                    {
                        tempControl[changeId] -= changeValue;
                    }
                    else
                    {
                        tempControl[changeId] += changeValue;
                    }
                    Pop[Pop.Count-1].IndividList[index].I[i].Group[j].setLightControls(tempControl);
                }
            }
        }
        public void Crossover()
        {
            for (int i = 0; i < Var.JncNumber; i++)
            {
                Pop[Pop.Count-1].IndividList[0].I[i].setTo(Pop[Pop.Count - 2].IndividList[Var.varRandom.getRandomNr(0, 4)].I[i]);
            }
        }
    }
}
